use thiserror::Error;

use aspect_codec::prelude::*;
use aspect_ident::DeviceIdent;

pub const ERR_OTHER: i16 = 1000;
pub const ERR_USER_EXISTS: i16 = 1100;
pub const ERR_USERNAME_SHORT: i16 = 1101;
pub const ERR_USERNAME_INV: i16 = 1102;
pub const ERR_PASSWORD_INSUFF: i16 = 1103;
pub const ERR_PASSWORD_INVALID: i16 = 1104;
pub const ERR_MISSING_EXTRA_CRED: i16 = 1105;
pub const ERR_IDENT_CLAIMED: i16 = 1106;

#[derive(Debug, Error)]
pub enum AspProtoError {
    #[error("other error: {0}")]
    Other(String),

    #[error("user '{0}' exists")]
    UserExists(String),

    #[error("username '{0}' too short")]
    UsernameTooShort(String),

    #[error("username '{0}' invalid")]
    UsernameInvalid(String),

    #[error("password insufficient")]
    PasswordInsufficient(()),

    #[error("invalid password")]
    InvalidPassword(()),

    #[error("missing extra credential")]
    MissingExtraCred(String),

    #[error("ident '{0}' already claimed")]
    IdentityAlreadyClaimed(DeviceIdent),
}

impl AspProtoError {
    pub fn server<T: ToString>(v: T) -> Self {
        Self::Other(v.to_string())
    }
}

aspect_codec::inst_enum_codec! {
    AspProtoError u8 {
        0 => Other(field String),
        1 => UserExists(field String),
        2 => UsernameTooShort(field String),
        3 => UsernameInvalid(field String),
        4 => PasswordInsufficient(field ()),
        5 => InvalidPassword(field ()),
        6 => MissingExtraCred(field String),
        7 => IdentityAlreadyClaimed(field DeviceIdent)
    }
}

impl From<AspProtoError> for ecksport_rpc::errors::RpcError {
    fn from(value: AspProtoError) -> Self {
        use AspProtoError::*;
        let code = match value {
            UserExists(_) => ERR_USER_EXISTS,
            UsernameTooShort(_) => ERR_USERNAME_SHORT,
            UsernameInvalid(_) => ERR_USERNAME_INV,
            PasswordInsufficient(_) => ERR_PASSWORD_INSUFF,
            InvalidPassword(_) => ERR_PASSWORD_INVALID,
            MissingExtraCred(_) => ERR_MISSING_EXTRA_CRED,
            IdentityAlreadyClaimed(_) => ERR_IDENT_CLAIMED,
            _ => ERR_OTHER,
        };

        ecksport_rpc::errors::RpcError::server(code, value)
    }
}
